<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        li {
            list-style: none;
        }

        #title {
            line-height: 40px;
            height: 40px;
        }

        .list li {
            height: 32px;
            line-height: 32px;
            border: 1px dashed #333;
        }

        .list li+li {
            border-top: 0;
        }

        .button-group {
            margin-top: 10px;
        }

        .button-group button {
            height: 30px;
            padding: 0 10px;
        }
    </style>
</head>

<body>
    <div class="wrap">
        <h3 id="title">dom操作封装</h3>
        <ul class="list">
            <li class="first">01</li>
            <li>02</li>
            <li>03</li>
            <li>04</li>
            <li>05</li>
            <li>06</li>
        </ul>
    </div>
    <div class="button-group">
        <button class="setHtml">html</button>
        <button class="hide">hide</button>
        <button class="show">show</button>
        <button class="css">css</button>
        <button class="width">width</button>
        <button class="attr">attr</button>
        <button class="prop">prop</button>
    </div>
</body>
<script>

    /* 
         //  // css选择器(字符串)
        new GetEle(".list li");

        // 单个原生dom元素 => 转化为对应的实例化对象
        var first = document.getElementsByClassName("first")[0];
        new GetEle(first);  // [first]

        // 多元素 (传入一个DOM元素的集合) => 转化为对应的实例化对象
        var liList = document.getElementsByTagName("li"); // [li,li,li,li,li,li]
        new GetEle(liList);
    
    */
    function GetEle(selector) {
        // {} 
        // {}[[Prototype]] = GetEle.prototype;
        // this -> {}

        // 多样化获取元素 => 将获取的元素放到集合中 
        if (typeof selector == "string") {// ".list li"
            // 接收一个css选择器  把获取的元素集合存储到实例化对象中
            var list = document.querySelectorAll(selector);  // {}.list=[li,li,li,li]
        } else if (selector instanceof Element) {
            // 接收的是单个dom元素 selector => li.first  => 没有集合就自己建一个
            var list = [selector];  // [li.first]  
        } else if (typeof selector == "object" && selector.length >= 0) {
            // 传入一个dom元素的集合   [li,li,li,li]
            var list = selector;
        }

        // list是伪数组 -> 有自己的原型属性[[Prototype]]指向,且改变原型属性指向后 数组结构不存在

        list = Array.from(list); // 转真数组 => 赋值原值

        Object.setPrototypeOf(list, GetEle.prototype); // 既保留数组结构,又能使用GetEle.prototype的方法

        return list;  // list = [li,li,li,li]
    }






    // text()  获取和设置实例化对象(集合)中的元素的text内容
    // text("11111")  => 有内容就设置
    // text()         => 没有内容就获取
    GetEle.prototype.text = function (con) {
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // arguments -> 函数调用时实际参数的集合   lenth-> 参数的个数
        if (arguments.length >= 1) { // 有至少一个参数 就设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.textContent = con;
            // }

            // this -> 调用此方法的实例化对象 -> 可以使用GetEle.prototype上所有的方法
            this.each((ele, i) => {
                ele.textContent = con;
            })

        } else { // 没有参数  获取
            return this[0].textContent;
        }
    }

    // html()  获取和设置实例化对象(集合)中的元素的html内容
    // html("11111")  => 有内容就设置
    // html()         => 没有内容就获取
    GetEle.prototype.html = function (con) {
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // arguments -> 函数调用时实际参数的集合   lenth-> 参数的个数
        if (arguments.length >= 1) { // 有至少一个参数 就设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.innerHTML = con;
            // }

            this.each((ele, i) => {
                ele.innerHTML = con;
            })
        } else { // 没有参数  获取
            return this[0].innerHTML;
        }
    }


    // val()  获取和设置实例化对象(集合)中的元素的val内容
    // val("11111")  => 有内容就设置
    // val()         => 没有内容就获取
    GetEle.prototype.val = function (con) {
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // arguments -> 函数调用时实际参数的集合   lenth-> 参数的个数
        if (arguments.length >= 1) { // 有至少一个参数 就设置
            // for (var i = 0; i < this.length; i++) {
            //     var ele = this[i];
            //     ele.value = con;
            // }
            this.each((ele, i) => {
                ele.value = con;
            })
        } else { // 没有参数  获取
            return this[0].value;
        }
    }

    // hide()   将实例化对象(集合)中的元素隐藏
    // show()   将实例化对象(集合)中的元素隐藏


    GetEle.prototype.hide = function (con) {
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.style.display = "none";
        // }
        this.each((ele, i) => {
            ele.style.display = "none";
        })

    }

    GetEle.prototype.show = function (con) {
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // for (var i = 0; i < this.length; i++) {
        //     var ele = this[i];
        //     ele.style.display = "block";
        // }

        this.each((ele, i) => {
            ele.style.display = "block";
        })
    }

    // 封装的遍历方法
    GetEle.prototype.each = function (callback) { // 形参callback接收了一个函数
        // this -> 调用此方法的实例化对象   list=[li,li,li,li]
        // var callback = function (item, index, array) {  // 传入的函数
        //     console.log(item, index, array);
        // }

        // 每次循环执行传入的函数
        for (var i = 0; i < this.length; i++) {
            // this[i]
            // i
            // this
            callback(this[i], i, this);
        }

    }


    // on(type,callback) -> 添加事件监听
    GetEle.prototype.on = function (type, callback) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        this.each(function (ele, i) {
            ele.addEventListener(type, callback);
        })
    }

    // 基于on方法二次封装 => 对象事件类型绑定事件
    GetEle.prototype.click = function (callback) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        this.on("click", callback);
    }
    GetEle.prototype.dblclick = function (callback) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        this.on("dblclick", callback);
    }
    GetEle.prototype.mousedown = function (callback) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        this.on("mousedown", callback);
    }


    // off(type,callback) -> 删除事件监听
    GetEle.prototype.off = function (type, callback) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        this.each(function (ele, i) {
            ele.removeEventListener(type, callback);
        })
    }

    // css()   获取和设置集合中元素的样式
    // css("width","200px")  有两个参数就设置
    // css({width: "200px",height: "200px"})  传入一个对象参数进行设置

    // css("width")          有一个参数就获取
    GetEle.prototype.css = function (cssKey, cssVal) {
        // this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        if (arguments.length >= 2) {  // 有两个参数就设置
            this.each(function (ele, i) {
                ele.style[cssKey] = cssVal;
            })

        } else if (arguments.length >= 1) { // 有一个参数就获取
            // 有一个参数  -> cssKey接收 (字符串/对象)

            if (typeof cssKey == "string") {
                // 多元素默认获取集合中的第一个
                var ele = this[0];
                if (window.getComputedStyle) {
                    return window.getComputedStyle(ele)[cssKey];
                } else {
                    return ele.currentStyle[cssKey];
                }
            } else if (typeof cssKey == "object") {
                // cssKey => 对象 => {width: "200px",height: "200px"}
                for (var key in cssKey) {
                    var val = cssKey[key];
                    // this.each(function (ele, i) {
                    //     ele.style[key] = val;
                    // })
                    this.css(key, val);
                }


            }

        } else {
            // 创建一个错误对象  -> 包含错误提示
            var err = new Error("Please pass in at least one parameters");
            throw err;  // 抛出错误 -> 代码将会显示红色报错 -> 后续代码也不执行
        }
    }


    // width()  获取和设置集合中元素的宽度
    // width("200px")  width("200")  有参数就设置
    // width()         没有参数就获取
    GetEle.prototype.width = function (val) {
        //  this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        if (arguments.length >= 1) { // 有参数就设置
            // val 宽度的具体值
            // 有单位  => 直接设置  200.55 px % pt em rem vw vh vmin vmax
            // 没有单位 默认px单位   200 => px

            var reg = /(px|%|pt|em|rem|vw|vh|vmin|vmax)$/
            if (reg.test(val)) {  //以 px等单位中的一个结尾
                this.css("width", val);  //设置宽度
            } else {
                this.css("width", val + "px");
            }
        } else { // 没有参数就获取
            return parseFloat(this.css("width"));   //取值时去单位
        }
    }


    // height()  获取和设置集合中元素的宽度
    // height("200px")  height("200")  有参数就设置
    // height()         没有参数就获取
    GetEle.prototype.height = function (val) {
        //  this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        if (arguments.length >= 1) { // 有参数就设置
            // val 宽度的具体值
            // 有单位  => 直接设置  200.55 px % pt em rem vw vh vmin vmax
            // 没有单位 默认px单位   200 => px

            var reg = /(px|%|pt|em|rem|vw|vh|vmin|vmax)$/
            if (reg.test(val)) {  //以 px等单位中的一个结尾
                this.css("height", val);  //设置宽度
            } else {
                this.css("height", val + "px");
            }
        } else { // 没有参数就获取
            return parseFloat(this.css("height"));   //取值时去单位
        }
    }

    // attr()   获取和设置集合中元素的属性节点
    // attr("class","one two three")  两个参数就设置
    // attr("class")  一个参数就获取
    GetEle.prototype.attr = function (key, val) {
        //  this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        if (arguments.length >= 2) {
            this.each((ele, i) => {
                ele.setAttribute(key, val);
            })
        } else if (arguments.length >= 1) {
            // 如果有多个参数默认返回集合中的第一个
            return this[0].getAttribute(key);
        } else {
            // 创建一个错误对象  -> 包含错误提示
            var err = new Error("Please pass in at least one parameters");
            throw err;  // 抛出错误 -> 代码将会显示红色报错 -> 后续代码也不执行
        }

    }

    // prop()   获取和设置集合中元素节点的内置属性 (property   Object.defineProperty())
    // prop("className", "one two three")  两个参数就设置  ele.className = "xxx"
    // prop("className")  一个参数就获取   ele.className
    GetEle.prototype.prop = function (key, val) {
        //  this -> 调用此方法的实例化对象 -> [li,li,li,li,li]
        if (arguments.length >= 2) {
            this.each((ele, i) => {
                ele[key] = val;
            })
        } else if (arguments.length >= 1) {
            // 如果有多个参数默认返回集合中的第一个
            return this[0][key];
        } else {
            // 创建一个错误对象  -> 包含错误提示
            var err = new Error("Please pass in at least one parameters");
            throw err;  // 抛出错误 -> 代码将会显示红色报错 -> 后续代码也不执行
        }
    }






    // 传入一个csss选择器 根据传入的参数快速获取实例化对象
    function $(selector) {  //  ".list li"
        return new GetEle(selector);  // new GetEle(".list li")
    }

    // $(".list li").text("hello");
    // $("#title").hide();


    console.log($(".list li"));


    function clickHandler() {
        console.log(this);
    }

    // $(".list li").on("click", clickHandler);
    // $(".list li").off("click", clickHandler);

    // $(".list li").click(function () {
    //     console.log(1111);
    // });

    // $(".list li").dblclick(function () {
    //     console.log(6666);
    // });


    $(".setHtml").click(function () {
        $(".list li").html("123123")
    })

    $(".hide").click(function () {
        $(".list li").hide()
    })

    $(".show").click(function () {
        $(".list li").show()
    })

    $(".css").click(function () {
        // 设置
        // $(".list li").css("width", "200px");
        // $(".list li").css("height", "200px");
        // $(".list li").css("background-color", "red");
        // $(".list li").css("text-align", "center");
        // $(".list li").css("line-height", "200px");

        // 设置 -> 传入一个对象参数 
        $(".list li").css({
            width: "200px",
            height: "200px",
            backgroundColor: "red",
            textAlign: "center",
            lineHeight: "200px",
        });

        // 获取
        // console.log($(".list li").css("width"));
        // console.log($(".list li").css("height"));
        // console.log($(".list li").css("backgroundColor"));
    })

    $(".width").click(function () {
        // $(".list li").width("50%");
        $(".list li").width(200);
        $(".list li").height(200);

        console.log($(".list li").width());
        console.log($(".list li").height());
    })

    $(".attr").click(function () {
        $(".list li").attr("class", "list-item");
        $(".list li").attr("title", "hello");
        $(".list li").attr("data-msg", "world");
    })

    $(".prop").click(function () {
        // console.log($(".list li").prop("className"));
        // console.log($(".list li").prop("title"));
        console.log($(".list li").prop("clientWidth"));

    })


    // tab栏切换
    $(".list li").click(function () {
        $(".list li").css({
            background: "#fff"
        })
        // this -> 被点击的li  -> 原生DOM元素 -> 不能直接使用 GetEle.prototype上的方法
        console.log($(this));
        $(this).css({ background: "red" });
    })














</script>

</html>